# coding=utf8


class MethodDescriptor:

    def __init__(self):
        self.parameter_types = []
        self.return_type = None

    def append_param_type(self, descriptor):
        self.parameter_types.append(descriptor)

    def __str__(self):
        return 'MethodDescriptor[parameters: %s,' \
               'return: %s]' % (self.parameter_types, self.return_type)


class DescriptorParserError(Exception):
    pass


class MethodDescriptorParser:

    def __init__(self, descriptor: str):
        self.offset = 0
        self.descriptor = descriptor
        self.method_descriptor = MethodDescriptor()

    def __read(self):
        next_offset = self.offset + 1
        char = self.descriptor[self.offset]
        self.offset = next_offset
        return char

    def __unread(self):
        self.offset -= 1

    def __start_parameters(self):
        if self.__read() != '(':
            raise DescriptorParserError('parse start parameters error')

    def __end_parameters(self):
        if self.__read() != ')':
            raise DescriptorParserError('parse end parameters error')

    def __parse_object_type(self):
        unread = self.descriptor[self.offset:]
        end_flag = unread.find(';')
        if end_flag == -1:
            raise DescriptorParserError('parse object type error')
        start = self.offset - 1
        end = self.offset + end_flag + 1
        self.offset = end
        return self.descriptor[start: end]

    def __parse_array_type(self):
        start = self.offset - 1
        self.__parse_type()
        end = self.offset
        return self.descriptor[start: end]

    def __parse_type(self):
        flag = self.__read()
        if flag in 'BCDFIJSZ':
            return flag
        elif flag == 'L':
            return self.__parse_object_type()
        elif flag == '[':
            return self.__parse_array_type()
        self.__unread()
        return ''

    def __parse_parameter_types(self):
        while True:
            param_type = self.__parse_type()
            if not param_type:
                break
            self.method_descriptor.append_param_type(param_type)

    def __parse_return_type(self):
        flag = self.__read()
        if flag == 'V':
            self.method_descriptor.return_type = 'V'
            return
        self.__unread()
        return_type = self.__parse_type()
        if return_type:
            self.method_descriptor.return_type = return_type
            return
        raise DescriptorParserError('parse return type error')

    def __finish(self):
        if self.offset != len(self.descriptor):
            raise DescriptorParserError('parse finish error')

    def parse(self) -> MethodDescriptor:
        self.__start_parameters()
        self.__parse_parameter_types()
        self.__end_parameters()
        self.__parse_return_type()
        self.__finish()
        return self.method_descriptor


__all__ = ['MethodDescriptor', 'MethodDescriptorParser']

if __name__ == '__main__':
    md = MethodDescriptorParser('(IDLjava/lang/Thread;)Ljava/lang/Object;')
    print(md.parse())
